package icu.xuyijie.springbootlearning1.chapter6;

import icu.xuyijie.springbootlearning1.chapter6.handler.LoginFailedHandler;
import icu.xuyijie.springbootlearning1.chapter6.handler.LoginSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 徐一杰
 * @date 2025/03/11 10:53
 * @description security 配置类
 */
@Configuration
public class SpringSecurityConfig {

    /**
     * 密码加密器，加盐的 MD5 算法，单向，只能加密，不能解密
     * @return 加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 从数据库取出数据
//    @Bean
//    public UserDetailsService userDetailsService() {
//        return new MyUserDetailsService();
//    }

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

        // 意思是往列表里假如一个角色，必须是 ROLE_ 开头
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_student"));
        // 不加 ROLE_ 前缀，加入的就不是角色，而是一条权限
        grantedAuthorities.add(new SimpleGrantedAuthority("delete"));

        manager.createUser(
                new User(
                        "xyj",
                        // 因为 security 存储的用户密码都只能是加密过的，所以我们们手动加密一下方便对比
                        passwordEncoder().encode("123456"),
                        // 角色、权限列表
                        grantedAuthorities
                )
        );

        return manager;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        // 查询登陆用户信息
        httpSecurity.userDetailsService(userDetailsService());

        // 根据规则来匹配各个接口的访问限制（如果接口上标记的有 @PreAuthorize 注解，那么以注解为准）
        httpSecurity.authorizeHttpRequests(http ->
            http
                    // html 和 css 文件不需要登录就能访问
                    .requestMatchers("*.html", "*.css").permitAll()
                    // /test 开头的接口不需要登录就能访问
                    .requestMatchers("/test/**").permitAll()
                    .requestMatchers("/actuator/**").permitAll()
                    // /test2 开头的接口，需要拥有 delete 或 add 权限才能访问
                    .requestMatchers("/test2/**").hasAnyAuthority("delete", "add")
                    // /test3 开头的接口，需要 student 角色才能访问
                    .requestMatchers("/test3/**").hasRole("student")
        );

        // 配置登录界面的跳转（前后端不分离）
        //httpSecurity.formLogin(Customizer.withDefaults())

        // 配置自定义的登录逻辑(前后端分离模式)
        httpSecurity.formLogin(form ->
                form
                        // 登录页面跳转
                        .loginPage("/login")
                        .permitAll()
                        // 登录时填写的用户名和密码的参数名
                        .usernameParameter("username")
                        .passwordParameter("password")
                        // 处理登录成功的逻辑
                        .successHandler(new LoginSuccessHandler())
                        // 处理登录失败的逻辑
                        .failureHandler(new LoginFailedHandler())
        );

        return httpSecurity.build();
    }

}
